package ace.cmp.spring.data.jdbc.querydsl.rsql.core;

import com.querydsl.core.types.ExpressionUtils;
import com.querydsl.core.types.Predicate;
import com.querydsl.sql.RelationalPath;
import cz.jirutka.rsql.parser.ast.ComparisonNode;
import cz.jirutka.rsql.parser.ast.LogicalNode;
import cz.jirutka.rsql.parser.ast.LogicalOperator;
import cz.jirutka.rsql.parser.ast.Node;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author caspar
 * @date 2023/4/14 9:43
 */
public class RsqlQuerydslFactory {
  private final RsqlPredicateResolver resolver;

  public RsqlQuerydslFactory(RsqlPredicateResolver resolver) {
    this.resolver = resolver;
  }

  public RsqlQuerydslFactory(Class... entityTypes) {
    this.resolver = new RsqlPredicateResolver(entityTypes);
  }

  public RsqlQuerydslFactory(RelationalPath<?>... relationalPaths) {
    this.resolver = new RsqlPredicateResolver(relationalPaths);
  }

  public Predicate create(Node node) {
    if (node instanceof LogicalNode) {
      return create((LogicalNode) node);
    }
    if (node instanceof ComparisonNode) {
      return create((ComparisonNode) node);
    }
    return null;
  }

  public Predicate create(LogicalNode logicalNode) {
    List<Predicate> predicates =
        logicalNode.getChildren().stream()
            .map(node -> create(node))
            .filter(Objects::nonNull)
            .collect(Collectors.toList());

    Predicate predicate = predicates.get(0);
    if (logicalNode.getOperator() == LogicalOperator.AND) {
      for (int i = 1; i < predicates.size(); i++) {
        predicate = ExpressionUtils.and(predicate, predicates.get(i));
      }
    } else if (logicalNode.getOperator() == LogicalOperator.OR) {
      for (int i = 1; i < predicates.size(); i++) {
        predicate = ExpressionUtils.or(predicate, predicates.get(i));
      }
    }

    return predicate;
  }

  public Predicate create(ComparisonNode comparisonNode) {
    return resolver.resolve(comparisonNode);
  }
}
